package com.info33.platform.common.util;

import com.info33.platform.common.exception.ResultException;
import com.info33.platform.system.util.enums.QrLoginStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * @author debj
 * @version 1.0
 * @date 2021/12/16 14:18
 */
@Slf4j
@Component
public class RedisUtils {

    /**
     * 验证码前缀
     */
    private static final String MESSAGE_CODE_PREFIX = "mobileCodePrefix:";

    /**
     * 密码错误计数前缀
     */
    private static final String PWD_ERR_COUNT_PREFIX = "loginPasswordCount:";

    /**
     * 系统二维码扫码登录前缀
     */
    private static final String SYSTEM_LOGIN_QR = "systemLoginQr:";

    private final RedisTemplate<String, Object> redisTemplate;

    @Autowired
    public RedisUtils(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 获取 redis 操作对象
     *
     * @return redisTemplate
     */
    public RedisTemplate<String, Object> getRedisTemplate() {
        return redisTemplate;
    }

    /**
     * 根据手机号发送验证码，同时存储至 redis 中
     *
     * @param mobile 手机号
     */
    public String pushCodeByMobile(String mobile) {
        // 校验是否已经发送
        String redisKey = MESSAGE_CODE_PREFIX + mobile;
        Boolean hasKey = redisTemplate.hasKey(redisKey);
        if (!ObjectUtils.isEmpty(hasKey)
                && Boolean.TRUE.equals(hasKey)) {
            throw new ResultException("验证码已发送，请稍后重试");
        }
        // 发送验证码
        int code = new Random().nextInt(900000) + 100000;
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        valueOperations.set(redisKey, code * 10 + 1, 3, TimeUnit.MINUTES);
        log.info("code ----- " + code);
        try {
            SendSms.sendCode(mobile, code);
        } catch (Exception e) {
            e.printStackTrace();
            valueOperations.getOperations().delete(redisKey);
            throw new ResultException();
        }
        return "验证码发送成功，3 分钟之内有效";
    }


    /**
     * 校验验证码
     *
     * @param mobile 手机号
     * @param code   验证码
     */
    public void checkCodeByMobile(String mobile, Integer code) {
        String redisKey = MESSAGE_CODE_PREFIX + mobile;
        Long increment = redisTemplate.opsForValue().increment(redisKey, 1);
        if (ObjectUtils.isEmpty(increment)) {
            throw new ResultException();
        }
        String stringCode = String.valueOf(increment);
        int end = Integer.parseInt(stringCode.substring(stringCode.length() - 1));
        if (end == 1 || end > 4) {
            // 1 代表是首次发送，2-4 代表三次尝试
            redisTemplate.delete(redisKey);
            throw new ResultException("验证码已过期，请重新获取验证码");
        }
        if (!code.equals(Integer.parseInt(stringCode.substring(0, stringCode.length() - 1)))) {
            throw new ResultException("验证码不正确，" + ((4 - end) == 0 ? "该验证码已失效，请稍后再试" : "剩余" + (4 - end) + "次机会"));
        }
        redisTemplate.delete(redisKey);
    }

    /**
     * 检查密码登录错误次数
     *
     * @param loginName 登录名
     * @param loginType 登录类别
     */
    public void checkPwdErrCount(String loginName, String loginType) {
        String redisKey = PWD_ERR_COUNT_PREFIX + loginType + ":" + loginName;
        ValueOperations<String, Object> redisOfv = redisTemplate.opsForValue();
        // 校验是否可登录
        Object o = redisOfv.get(redisKey);
        if (!ObjectUtils.isEmpty(o)
                && Integer.parseInt(o.toString()) > 6) {
            Long expire = redisTemplate.getExpire(redisKey, TimeUnit.HOURS);
            expire = ObjectUtils.isEmpty(expire) ? 1L : expire + 1L;
            throw new ResultException("此账号密码登录失败次数超 六 次，已经被冻结，请 "
                    + expire
                    + " 小时后重试");
        }
    }

    /**
     * 清除密码错误计数
     *
     * @param loginName 登录名
     * @param loginType 登录类别
     */
    public void clearPwdErrCount(String loginName, String loginType) {
        redisTemplate.delete(PWD_ERR_COUNT_PREFIX + loginType + ":" + loginName);
    }

    /**
     * 清除密码错误计数
     *
     * @param loginName 登录名
     * @param loginType 登录类别
     */
    public String addPwdErr(String loginName, String loginType) {
        String redisKey = PWD_ERR_COUNT_PREFIX+ loginType + ":" + loginName;
        ValueOperations<String, Object> redisOfv = redisTemplate.opsForValue();
        Long increment = redisOfv.increment(redisKey, 1);
        redisTemplate.expire(redisKey, 10, TimeUnit.HOURS);
        if (ObjectUtils.isEmpty(increment)) {
            return "账号或密码错误，密码输错6次将被锁定";
        }
//        return ((7L - increment) > 0 ? ("密码错误, 剩余：" + (7L - increment) + "次机会") : "此账号密码登录失败次数超 六 次，已经被冻结，请 10 小时后重试");
        return ((7L - increment) > 0 ? "账号或密码错误，密码输错6次将被锁定" : "此账号密码登录失败次数超 六 次，已经被冻结，请 10 小时后重试");
    }

    /**
     * 保存二维码 key 至 redis
     *
     * @param key            内容
     * @param generateqrcode
     */
    public void saveQrKey(String key, QrLoginStatusEnum generateqrcode) {
        redisTemplate.opsForValue().set(SYSTEM_LOGIN_QR + key, generateqrcode, 3, TimeUnit.MINUTES);
    }

    /**
     * 检查 redis 中的 qr 是否有效
     *
     * @param key key
     */
    public Object checkQrKey(String key) {
        if (!Boolean.TRUE.equals(redisTemplate.hasKey(SYSTEM_LOGIN_QR + key))) {
            throw new ResultException("二维码已过期，请刷新重试");
        }
        return redisTemplate.opsForValue().get(key);
    }

    /**
     * 通过键，将 token 存储至 redis 中
     *
     * @param key   key
     * @param token token
     */
    public void saveQrContent(String key, String token) {
        redisTemplate.opsForValue().set(SYSTEM_LOGIN_QR + key, token, 1, TimeUnit.MINUTES);
    }

    /**
     * 通过键，获取 qr 内的 token
     *
     * @param key 键
     * @return token
     */
    public Object getQrContent(String key) {
        Object object = redisTemplate.opsForValue().get(key);
        redisTemplate.delete(key);
        return object;
    }

}
